///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef __BUOLA_TRAITS_TYPENAME_H__
#define __BUOLA_TRAITS_TYPENAME_H__

#include <buola/buola.h>
#include <array>

#define DECLARE_TYPENAME_TRAITS(type,name)                                                       \
    template<>                                                                                   \
    struct MTypeName<type>                                                                       \
    {                                                                                            \
        static const char *Get()   {   return name;    }                                         \
    }

#define DECLARE_TYPENAME_TRAITS_TEMPLATE_T_N(temp,name)                                          \
    template<typename t1,std::size_t n1>                                                              \
    struct MTypeName<temp<t1,n1>>                                                               \
    {                                                                                            \
        static const char *Get()                                                                 \
        {                                                                                        \
            static const std::string lName=std::string(name)+'<'+MTypeName<t1>::Get()+','+               \
                 to_string(n1)+'>';                                                 \
            return lName.c_str();                                                                \
        }                                                                                        \
    }

#define DECLARE_TYPENAME_TRAITS_TEMPLATE_T_N_N(temp,name)                                        \
    template<typename t1,std::size_t n1,std::size_t n2>                                          \
    struct MTypeName<temp<t1,n1,n2>>                                                             \
    {                                                                                            \
        static const char *Get()                                                                 \
        {                                                                                        \
            static const std::string lName=std::string(name)+'<'+MTypeName<t1>::Get()+','+       \
                 to_string(n1)+','+to_string(n2)+'>';          \
            return lName.c_str();                                                                \
        }                                                                                        \
    }

#define DECLARE_TYPENAME_TRAITS_TEMPLATE_N_T(temp,name)                                          \
    template<int n1,typename t1>                                                                 \
    struct MTypeName<temp<n1,t1>>                                                               \
    {                                                                                            \
        static const char *Get()                                                                 \
        {                                                                                        \
            static const std::string lName=std::string(name)+'<'+to_string(n1)+','+         \
                 MTypeName<t1>::Get()+'>';                                                       \
            return lName.c_str();                                                                \
        }                                                                                        \
    }

#define DECLARE_TYPENAME_TRAITS_TEMPLATE_T(temp,name)                                            \
    template<typename t1>                                                                        \
    struct MTypeName<temp<t1>>                                                                  \
    {                                                                                            \
        static const char *Get()                                                                 \
        {                                                                                        \
            static const std::string lName=std::string(name)+'<'+MTypeName<t1>::Get()+'>';               \
            return lName.c_str();                                                                \
        }                                                                                        \
    }

namespace buola {

//get the name of a type
    
template<typename tType>
struct MTypeName;

template<typename tType>
struct MTypeName<tType*>
{
    static const char *Get()
    {
        static const std::string lName=std::string(MTypeName<tType>::Get())+'*';
        return lName.c_str();
    }
};

template<typename tType>
struct MTypeName<tType&>
{
    static const char *Get()
    {
        static const std::string lName=std::string(MTypeName<tType>::Get())+'&';
        return lName.c_str();
    }
};

template<typename tType>
struct MTypeName<const tType>
{
    static const char *Get()
    {
        static const std::string lName="const("+std::string(MTypeName<tType>::Get())+')';
        return lName.c_str();
    }
};

DECLARE_TYPENAME_TRAITS(void,"void");
DECLARE_TYPENAME_TRAITS(char,"char");
DECLARE_TYPENAME_TRAITS(bool,"bool");
DECLARE_TYPENAME_TRAITS(uint8_t,"uint8");
DECLARE_TYPENAME_TRAITS(int8_t,"int8");
DECLARE_TYPENAME_TRAITS(uint16_t,"uint16");
DECLARE_TYPENAME_TRAITS(int16_t,"int16");
DECLARE_TYPENAME_TRAITS(uint32_t,"uint32");
DECLARE_TYPENAME_TRAITS(int32_t,"int32");
DECLARE_TYPENAME_TRAITS(uint64_t,"uint64");
DECLARE_TYPENAME_TRAITS(int64_t,"int64");
DECLARE_TYPENAME_TRAITS(float,"float");
DECLARE_TYPENAME_TRAITS(double,"double");
DECLARE_TYPENAME_TRAITS(std::wstring,"std::wstring");
DECLARE_TYPENAME_TRAITS(std::string,"std::string");
DECLARE_TYPENAME_TRAITS_TEMPLATE_T(std::vector,"std::vector");
DECLARE_TYPENAME_TRAITS_TEMPLATE_T_N(std::array,"std::array");

//get the names of types as a comma-separated list

template<typename... tTypes>
struct MTypeListName;

template<>
struct MTypeListName<>
{
    static const char *Get()
    {
        return "";
    }
};

template<typename tOne>
struct MTypeListName<tOne>
{
    static const char *Get()
    {
        return MTypeName<tOne>::Get();
    }
};

template<typename tFirst,typename... tRest>
struct MTypeListName<tFirst,tRest...>
{
    static const char *Get()
    {
        static const std::string lName=std::string(MTypeName<tFirst>::Get())+','+
                                    MTypeListName<tRest...>::Get();
        return lName.c_str();
    }
};

template<typename tSignature>
struct MSignatureName;

template<typename tResult,typename...tArgs>
struct MSignatureName<tResult(tArgs...)>
{
    static const char *Get()
    {
        static const std::string lName=std::string(MTypeName<tResult>::Get())+'('+
                                           MTypeListName<tArgs...>::Get()+')';
        return lName.c_str();
    }
};

/*namespace buola*/ }

#endif
